var mxCodecRegistry =
{
	codecs: {},
	register: function(codec)
	{
		var name = mxUtils.getFunctionName(codec.template.constructor);
		mxCodecRegistry.codecs[name] = codec;
	},
	getCodec: function(ctor)
	{
		var codec = null;

		if (ctor != null)
		{
			var name = typeof ctor == "string" ? ctor : mxUtils.getFunctionName(ctor);
			codec = mxCodecRegistry.codecs[name];

			if (codec == null)
			{
				try
				{
					codec = new mxObjectCodec(new ctor());
					mxCodecRegistry.register(codec);
				}
				catch (e)
				{
				}
			}
		}
		return codec;
	}
};

function mxCodec(document)
{
	this.document = document || mxUtils.createXmlDocument();
	this.objects = {};
};
mxCodec.prototype.document = null;
mxCodec.prototype.objects = null;
mxCodec.prototype.encodeDefaults = false;
mxCodec.prototype.putObject = function(id, obj)
{
	this.objects[id] = obj;
	return obj;
};
mxCodec.prototype.getObject = function(id)
{
	var obj = null;

	if (id != null)
	{
		obj = this.objects[id];

		if (obj == null)
		{
			obj = this.lookup(id);

			if (obj == null)
			{
				var node = this.getElementById(id);

				if (node != null)
				{
					obj = this.decode(node);
				}
			}
		}
	}
	return obj;
};
mxCodec.prototype.lookup = function(id)
{
	return null;
}
mxCodec.prototype.getElementById = function(id, attr)
{
	attr = attr || 'id';
	var expr = '//*[@' + attr + '=\'' + id + '\']';
	return mxUtils.selectSingleNode(this.document, expr);
};
mxCodec.prototype.getId = function(obj)
{
	var id = null;

	if (obj != null)
	{
		id = this.reference(obj);

		if (id == null && obj.constructor == mxCell)
		{
			id = obj.getId();

			if (id == null)
			{
				id = mxCellPath.create(obj);

				if (id.length == 0)
				{
					id = 'root';
				}
			}
		}
	}
	return id;
};
mxCodec.prototype.reference = function(obj)
{
	return null;
};
mxCodec.prototype.encode = function(obj)
{
	var node = null;

	if (obj != null && obj.constructor != null)
	{
		var enc = mxCodecRegistry.getCodec(obj.constructor);

		if (enc != null)
		{
			node = enc.encode(this, obj);
		}
		else
		{
			if (isNode(obj))
			{
				node = (mxClient.IS_IE) ? obj.value.cloneNode(true) : this.document.importNode(obj.value, true);
			}
			else
			{
				mxLog.warn('mxCodec.encode: No codec for ' + mxUtils.getFunctionName(obj.constructor));
			}
		}
	}
	return node;
};
mxCodec.prototype.decode = function(node, into)
{
	var obj = null;

	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
	{
		var ctor = null;

		try
		{
			var ctor = eval(node.nodeName);
		}
		catch (err)
		{
			ctor = node.nodeName;
		}

		try
		{
			var dec = mxCodecRegistry.getCodec(ctor);

			if (dec != null)
			{
				obj = dec.decode(this, node, into);
			}
			else
			{
				obj = node.cloneNode(true);
				obj.removeAttribute('as');
			}
		}
		catch (err)
		{
			mxLog.debug('Cannot decode ' + node.nodeName + ': ' + err.message);
		}
	}
	return obj;
};
mxCodec.prototype.encodeCell = function(cell, node, isIncludeChildren)
{
	node.appendChild(this.encode(cell));

	if (isIncludeChildren == null || isIncludeChildren)
	{
		var childCount = cell.getChildCount();

		for (var i = 0; i < childCount; i++)
		{
			this.encodeCell(cell.getChildAt(i), node);
		}
	}
};
mxCodec.prototype.decodeCell = function(node, isRestoreStructures)
{
	var cell = null;

	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
	{
		var decoder = mxCodecRegistry.getCodec(node.nodeName);

		if (decoder == null)
		{
			decoder = mxCodecRegistry.getCodec(mxCell);
		}
		cell = decoder.decode(this, node);

		if (isRestoreStructures == null || isRestoreStructures)
		{
			var parent = cell.getParent();

			if (parent != null)
			{
				parent.insert(cell);
			}
			var source = cell.getTerminal(true);

			if (source != null)
			{
				source.insertEdge(cell, true);
			}
			var target = cell.getTerminal(false);

			if (target != null)
			{
				target.insertEdge(cell, false);
			}
		}
	}
	return cell;
};
mxCodec.prototype.setAttribute = function(node, attribute, value)
{
	if (attribute != null && value != null)
	{
		node.setAttribute(attribute, value);
	}
};